package netty.demo_05_GroupChat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Netty应用实例-群聊系统
 * 服务端 的 处理器
 *
 * @author chenyuan
 * @date 2020/11/9 16:31
 */
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
	/**
	 * 定义一个 Channel 线程组，管理所有的 Channel, 参数 执行器
	 * GlobalEventExecutor => 全局事件执行器
	 * INSTANCE => 表示是单例的
	 */
	private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
	//定义一个时间的输出格式
	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	/**
	 * 当连接建立之后，第一个被执行
	 * 一连接成功，就把当前的 Channel 加入到 ChannelGroup，并将上线消息推送给其他客户
	 */
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		// 获取当前 Channel
		Channel channel = ctx.channel();
		// 将该客户上线的信息，推送给其他在线的 客户端
		// 该方法，会将 ChannelGroup 中所有的 Channel 遍历，并发送消息
		Date date = new Date(System.currentTimeMillis());
		channelGroup.writeAndFlush("[客户端] [" + dateFormat.format(date) + "] " + channel.remoteAddress() + " 加入群聊~\n");
		// 将当前 Channel 加入 ChannelGroup
		channelGroup.add(channel);
	}

	/**
	 * 当断开连接激活，将 XXX 退出群聊消息推送给当前在线的客户
	 * 当某个 Channel 执行到这个方法，会自动从 ChannelGroup 中移除
	 */
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		Date date = new Date(System.currentTimeMillis());
		channelGroup.writeAndFlush("[客户端] [" + dateFormat.format(date) + "] " + ctx.channel().remoteAddress() + " 退出群聊~\n");
		// 输出 ChannelGroup 的大小
		System.out.println("==== ChannelGroup-Size : " + channelGroup.size());
	}

	/**
	 * 当 Channel 处于一个活动的状态激活，可以提示 XXX 上线
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		Date date = new Date(System.currentTimeMillis());
		System.out.println("[" + dateFormat.format(date) + "] " + ctx.channel().remoteAddress() + " 已上线~\n");
	}

	/**
	 * 当 Channel 处于不活动的状态激活，提示 XXX 离线
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		Date date = new Date(System.currentTimeMillis());
		System.out.println("[" + dateFormat.format(date) + "] " + ctx.channel().remoteAddress() + " 已下线~\n");
	}

	/**
	 * 读取数据，并把读取到的数据转发给所有 客户
	 */
	@Override
	protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
		// 获取到 当前 Channel
		Channel channel = channelHandlerContext.channel();

		Date date = new Date(System.currentTimeMillis());
		//遍历 ChannelGroup 根据不同的情况，推送不同的消息
		channelGroup.forEach(ch -> {
			if (ch != channel) {//遍历到的当前的 ch 不是发消息的 Channel
				ch.writeAndFlush("[客户端] [" + dateFormat.format(date) + "] " + channel.remoteAddress() + " 发送了消息 ：" + s + "\n");
			} else {// 当前 ch 就是发消息的那个客户
				ch.writeAndFlush("[自己] [" + dateFormat.format(date) + "] " + s + " | 发送成功~\n");
			}
		});
	}

	/**
	 * 异常处理
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		// 关闭该通道
		ctx.close();
	}
}
